
#include "include.h"

char mcu_data[128];

/**
 * @brief 这里有个可以改进的地方
 * dev_alloc_skb函数内部已经rx_skb->data=malloc()了
 * 所以我们外面只能用memcpy将data复制到rx_skb->data
 * 但是如果可以自己实现dev_alloc_skb函数,即可避免memcpy.
 *
 * @param irq
 * @param dev_id
 * @return irqreturn_t
 */
static irqreturn_t spi_irq_handler(int irq, void *dev_id)
{
	struct w800_dev *w800dev = (struct w800_dev *)dev_id;
	complete(&w800dev->ah_done);
	return IRQ_HANDLED;
}

static int w800_ah(void *arg) // 接收数据
{
	unsigned int timeout;
	struct w800_dev *w800dev;
	struct sk_buff *rx_skb;
	struct spi_transfer *spi_t;
	char *_rx_buf, *_tx_buf, tmp;

	w800dev = arg;

	spi_t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
	spi_t->speed_hz = 50000000;
	spi_t->rx_buf = kzalloc(2000, GFP_KERNEL);
	spi_t->tx_buf = kzalloc(2000, GFP_KERNEL);
	_rx_buf = (char *)(spi_t->rx_buf);
	_tx_buf = (char *)(spi_t->tx_buf);

	while (1)
	{
		reinit_completion(&w800dev->ah_done);
		timeout = wait_for_completion_timeout(&w800dev->ah_done, msecs_to_jiffies(5000));

		if (timeout)
		{
			// 开始从单片机读取数据帧
			unsigned int read_len;
			while (1)
			{
				// 获取数据长度
				spi_t->len = 3;
				_tx_buf[0] = Read_reg(RX_DAT_LEN);
				w800_spiex(w800dev, spi_t);
				read_len = _rx_buf[2] << 8 | _rx_buf[1];
				if (read_len > 2048)
					break;
				spi_t->len = read_len + 1;
				// if(read_len<32)
				// {
				// 	_tx_buf[0] = Read_reg(DAT_PORT1);
				// 	w800_spiex(w800dev,spi_t);
				// 	memcmp(mcu_data,_rx_buf,spi_t->len);
				// 	complete(&w800dev->cmd_done);
				// 	break;
				// }

				// 构造接收skb
				rx_skb = dev_alloc_skb(read_len);
				if (rx_skb == NULL)
				{
					printk("rx_skb==NULL");
					break;
				}
				else if (rx_skb->data == NULL)
				{
					printk("rx_skb->data==NULL");
					break;
				}

				// 获取上传的数据
				_tx_buf[0] = Read_reg(DAT_PORT1);
				spi_t->rx_buf = rx_skb->data - 1;
				// tmp = ((char *)spi_t->rx_buf)[0];  //保存data上一个字节的数据
				w800_spiex(w800dev, spi_t);
				//((char *)spi_t->rx_buf)[0] = tmp;  //回复data上一个的数据
				spi_t->rx_buf = _rx_buf;

				rx_skb->len = read_len;
				rx_skb->dev = w800dev->w_net;
				rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
				rx_skb->protocol = eth_type_trans(rx_skb, w800dev->w_net);

				w800dev->w_net->stats.rx_packets++;
				w800dev->w_net->stats.rx_bytes += rx_skb->len;

				netif_rx(rx_skb);

				spi_t->len = 3;
				_tx_buf[0] = Read_reg(SPI_INT_HOST_STTS);
				w800_spiex(w800dev, spi_t);
				if (!(_rx_buf[1] & 0x01))
					break;
			}
		}
	}

	kfree(spi_t->tx_buf);
	kfree(spi_t->rx_buf);
	kfree(spi_t);

	return 0;
}

static int w800_probe(struct spi_device *spi)
{
	int ret, irq;
	struct w800_dev *w800dev;
	struct task_struct *ah_thread;

	w800dev = kzalloc(sizeof(struct w800_dev), GFP_KERNEL);

	dev_set_drvdata(&spi->dev, w800dev);

	w800dev->nd = spi->dev.of_node;
	w800dev->device = &spi->dev;
	w800dev->spi = spi;

	/*初始化spi_device*/
	spi->mode = SPI_MODE_0;
	spi_setup(spi);

	init_completion(&w800dev->ah_done);
	init_completion(&w800dev->cmd_done);

	spinet_init(w800dev);

	ah_thread = kthread_create(w800_ah, w800dev, "w800_ah");
	w800dev->ah_th = ah_thread;
	wake_up_process(ah_thread);

	irq = irq_of_parse_and_map(w800dev->nd, 0);
	if (irq < 0)
	{
		ret = -ENXIO;
		return ret;
	}
	ret = devm_request_irq(w800dev->device, irq, spi_irq_handler, 0, "spi_wlan", w800dev);
	if (ret)
	{
		printk("irq无法请求");
		return -EINVAL;
	}

	return 0;
}

static int w800_remove(struct spi_device *spi)
{
	struct w800_dev *w800dev;

	w800dev = dev_get_drvdata(&spi->dev);

	rm_spinet_init(w800dev);

	return 0;
}

/*传统匹配*/
struct spi_device_id w800_id[] = {
	{"WinnerMicro,w800", 0},
	{}};

/*设备树匹配*/
static const struct of_device_id w800_of_match[] = {
	{.compatible = "WinnerMicro,w800"},
	{}};

struct spi_driver w800_driver = {
	.probe = w800_probe,
	.remove = w800_remove,
	.driver = {
		.name = "w800_spi",
		.owner = THIS_MODULE,
		.of_match_table = w800_of_match,
	},
	.id_table = w800_id,
};

static int __init w800_init(void)
{
	spi_register_driver(&w800_driver);
	return 0;
}

static void __exit w800_exit(void)
{
	spi_unregister_driver(&w800_driver);
}

module_init(w800_init);
module_exit(w800_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("cyy");
MODULE_VERSION("0.0.1");
MODULE_DESCRIPTION("SPI-W800 Driver");
